﻿using System.Reflection;
using System.Collections.Generic;

namespace Hont
{
    /// <summary>
    /// 参数沙盒工具。可储存目标对象特定时期的状态并随时恢复。
    /// </summary>
    /// <typeparam name="T">目标对象类型</typeparam>
    public class ArgumentSandbox<T>
    {
        bool mEnableSerialize;
        bool mEnableDeepCopy;
        Queue<object> mValuesQueue;
        List<string> mTargetArgumentList;
        bool mIsSpecifiedOrIgnore;

        /// <summary>
        /// 供Xml序列化的数组结构。
        /// </summary>
        public object[] SerializeValuesQueueArray { get { return mEnableSerialize ? mValuesQueue.ToArray() : new object[0]; } set { mValuesQueue = new Queue<object>(value); } }

        /// <summary>
        /// 是否开启深拷贝，需考虑目标内部是否存在循环引用的情况。
        /// </summary>
        public bool EnableDeepCopy { get { return mEnableDeepCopy; } set { mEnableDeepCopy = value; } }

        /// <summary>
        /// 是否开启序列化，如果为否则不序列化任何数据。
        /// </summary>
        public bool EnableSerialize { get { return mEnableSerialize; } set { mEnableSerialize = value; } }


        public ArgumentSandbox()
        {
            mEnableSerialize = true;
            mEnableDeepCopy = true;
            mValuesQueue = new Queue<object>();
            mTargetArgumentList = new List<string>();
        }

        /// <summary>
        /// 设置指定字段关键字，指定模式下未设置的字段都不会被储存下来。
        /// </summary>
        public void SetSpecifiedArgumentMode(string[] argNameArr)
        {
            mTargetArgumentList.Clear();
            mTargetArgumentList.AddRange(argNameArr);

            mIsSpecifiedOrIgnore = true;
        }

        /// <summary>
        /// 设置忽略字段关键字，忽略模式下已忽略的字段不会被储存下来。
        /// </summary>
        public void SetIgnoreArgumentMode(string[] argNameArr)
        {
            mTargetArgumentList.Clear();
            mTargetArgumentList.AddRange(argNameArr);

            mIsSpecifiedOrIgnore = false;
        }

        /// <summary>
        /// 将目标对象的当前状态执行录入。
        /// </summary>
        public void Record(T obj)
        {
            if (obj.GetType().IsValueType)
            {
                Enqueue(obj);
            }
            else
            {
                mValuesQueue.Clear();
                Record(null, obj, true);
            }
        }

        /// <summary>
        /// 对目标对象执行恢复。
        /// </summary>
        /// <param name="modifiedObj"></param>
        public void Recovery(T modifiedObj)
        {
            Recovery(ref modifiedObj);
        }

        /// <summary>
        /// 对目标对象执行恢复(注意值类型需调用ref重载)。
        /// </summary>
        /// <param name="modifiedObj"></param>
        public void Recovery(ref T modifiedObj)
        {
            if (modifiedObj.GetType().IsValueType && mValuesQueue.Count > 0)
            {
                modifiedObj = (T)Dequeue();
            }
            else
            {
                Recovery(null, modifiedObj, true);
            }
        }

        void Record(object parentObj, object obj, bool isRootObject)
        {
            var value = obj;

            if (obj is FieldInfo)
            {
                var fieldInfo = obj as FieldInfo;
                value = fieldInfo.GetValue(parentObj);
            }

            if (obj is PropertyInfo)
            {
                var propInfo = obj as PropertyInfo;
                if (propInfo.CanRead && propInfo.CanWrite)
                    value = propInfo.GetValue(parentObj, null);
            }

            if (!isRootObject)
                Enqueue(value);

            if (value != null && !value.GetType().IsValueType && !(value is string))//深度查找
            {
                var fieldInfos = value
                    .GetType()
                    .GetFields(BindingFlags.Instance | BindingFlags.Public);

                for (int i = 0; i < fieldInfos.Length; i++)
                {
                    var item = fieldInfos[i];

                    if (isRootObject && !CheckArgument(item.Name)) continue;
                    if (!isRootObject && !mEnableDeepCopy) continue;

                    Record(value, item, false);
                }

                var propInfos = value
                   .GetType()
                   .GetProperties(BindingFlags.Instance | BindingFlags.Public);

                for (int i = 0; i < propInfos.Length; i++)
                {
                    var item = propInfos[i];

                    if (!item.CanWrite || !item.CanRead) continue;
                    if (isRootObject && !CheckArgument(item.Name)) continue;
                    if (!isRootObject && !mEnableDeepCopy) continue;

                    Record(value, item, false);
                }
            }
        }

        void Recovery(object parentObj, object modifiedObj, bool isRootObject)
        {
            var value = modifiedObj;
            var fieldInfo = modifiedObj as FieldInfo;
            var propInfo = modifiedObj as PropertyInfo;

            if (propInfo != null)
            {
                if (!propInfo.CanRead || !propInfo.CanWrite)
                    propInfo = null;
            }
            
            if (fieldInfo != null && mValuesQueue.Count > 0)
                fieldInfo.SetValue(parentObj, Dequeue());

            if (propInfo != null && mValuesQueue.Count > 0)
                propInfo.SetValue(parentObj, Dequeue(), null);

            if (fieldInfo != null)
                value = fieldInfo.GetValue(parentObj);

            if (propInfo != null)
            {
                if (propInfo.CanRead && propInfo.CanWrite)
                    value = propInfo.GetValue(parentObj, null);
            }

            if (value != null && !value.GetType().IsValueType && !(value is string))//深度查找
            {
                var fieldInfos = value
                                  .GetType()
                                  .GetFields(BindingFlags.Instance | BindingFlags.Public);

                for (int i = 0; i < fieldInfos.Length; i++)
                {
                    var item = fieldInfos[i];

                    if (isRootObject && !CheckArgument(item.Name)) continue;
                    if (!isRootObject && !mEnableDeepCopy) continue;

                    Recovery(value, item, false);
                }

                var propInfos = value
                    .GetType()
                    .GetProperties(BindingFlags.Instance | BindingFlags.Public);

                for (int i = 0; i < propInfos.Length; i++)
                {
                    var item = propInfos[i];

                    if (!item.CanWrite || !item.CanRead) continue;
                    if (isRootObject && !CheckArgument(item.Name)) continue;
                    if (!isRootObject && !mEnableDeepCopy) continue;

                    Recovery(value, item, false);
                }
            }
        }

        bool CheckArgument(string name)
        {
            if (mIsSpecifiedOrIgnore)
            {
                return mTargetArgumentList.Contains(name);
            }
            else
            {
                return !mTargetArgumentList.Contains(name);
            }
        }

        void Enqueue(object value)
        {
            //UnityEngine.Debug.Log("[Enqueue]value: " + value);
            mValuesQueue.Enqueue(value);
        }

        object Dequeue()
        {
            var dequeue = mValuesQueue.Dequeue();
            //UnityEngine.Debug.Log("[Dequeue]value: " + dequeue);
            return dequeue;
        }
    }
}
